import * as format from "ol/format";
import * as source from "ol/source";
import * as layer from "ol/layer";
import * as style from "ol/style";
import * as extent from "ol/extent";
import * as geom from "ol/geom";
import * as ol from "ol";
//  @ts-ignore
import DirectionImg from './fangxiang.png'
import {v4} from "uuid";
class ShipTrack {
  private uuid = v4()
  private infos: any = {}
  private map: any = null
  private historyPoints: any = []
  private realPoints: any = []
  private color = ''
  private trackLayer: any = null
  private pointLayer: any = null
  private directionLayer: any = null
  private historyPointCoordinates: any = []
  private realPointCoordinates: any = []
  private pointMap: any = new Map()
  private keyMapper: any = {}
  private functions = {
    zoom: () => {}
  }
  private zIndex
  private showTrackPoint = true
  private showTrackDirection = true
  private trackRadio = 30

  constructor({map, keys = {
    lon: 'shipLon',
    lat: 'shipLat',
  }, color = '', infos = {}}: any, zIndex = 400) {
    this.map = map
    this.color = color
    this.infos = infos
    this.keyMapper = keys
    this.zIndex = zIndex
  }
  init(historyPoints: any = [], realPoints: any = []) {
    if (!this.color) {
      this.color = randomColor(1)
    }
    historyPoints.forEach((v, i) => {
      this.pointMap.set(`${v[this.keyMapper.lon]}_${v[this.keyMapper.lat]}`, v)
      this.historyPointCoordinates.push([v[this.keyMapper.lon], v[this.keyMapper.lat]])
      this.historyPoints.push(v)
    });
    realPoints.forEach((v, i) => {
      this.pointMap.set(`${v[this.keyMapper.lon]}_${v[this.keyMapper.lat]}`, v)
      this.realPointCoordinates.push([v[this.keyMapper.lon], v[this.keyMapper.lat]])
      this.realPoints.push(v)
    });
    const lineF: any = new ol.Feature({
      geometry: new geom.LineString([...this.historyPointCoordinates, ...this.realPointCoordinates]),
    })
    this.trackLayer = new layer.VectorImage({
      source: new source.Vector({
        features: [lineF],
        wrapX: false,
      }),
      zIndex: this.zIndex,
    });
    this.trackLayer.set('layerName', this.uuid + '_track')
    this.map.addLayer(this.trackLayer);
    this.refreshStyle()
    this.functions.zoom = debounce(e => {
      this.refreshData()
    }, 300)
    this.map.getView().on('change:resolution', this.functions.zoom)
    return this
  }
  focus() {
    getShapeView(this.map, this.trackLayer.getSource().getFeatures()[0].getGeometry().getCoordinates())
  }
  show() {
    this.trackLayer.setVisible(true)
    this.pointLayer.setVisible(true)
    this.directionLayer.setVisible(true)
  }
  hide() {
    this.trackLayer.setVisible(false)
    this.pointLayer.setVisible(false)
    this.directionLayer.setVisible(false)
  }
  getSimplifyData() {
    const resolution = this.map.getView().getResolution()
    let radio = (this.trackRadio * resolution);
    if (this.map.getView().getZoom() == this.map.getView().getMaxZoom()) {
      radio = 0
    }
    const simplifyGeom: any = new geom.LineString([...this.historyPointCoordinates, ...this.realPointCoordinates]).simplify(radio)
    const simplifyCoor = simplifyGeom.getCoordinates()
    const directionFeatures: any = []
    const pointFeatures: any = []
    simplifyCoor.forEach((v, i) => {
      if (i > 0) {
        const last = simplifyCoor[i - 1]
        const dx = v[0] - last[0];
        const dy = v[1] - last[1];
        const rotation = Math.atan2(dy, dx) * -1;
        const directFeat = new format.WKT().readFeature(`POINT(${(v[0] + last[0]) / 2} ${(v[1] + last[1]) / 2})`)
        directFeat.set('rotation_', rotation)
        directionFeatures.push(directFeat)
      }
      const d = this.pointMap.get(`${v[0]}_${v[1]}`)
      const feat: any = new format.WKT().readFeature(`POINT(${v[0]} ${v[1]})`)
      feat.set('data_', d)
      feat.set('type_', 'track-point')
      pointFeatures.push(feat)
    });
    return {simplifyGeom, pointFeatures, directionFeatures}
  }
  refreshStyle() {
    this.map.getLayers().getArray().filter(v => [this.uuid + '_direction', this.uuid + '_point'].includes(v.get('layerName'))).forEach(v => {
      this.map.removeLayer(v)
    })
    const {simplifyGeom, pointFeatures, directionFeatures} = this.getSimplifyData()
    const trackFeat = this.trackLayer.getSource().getFeatures()[0]
    trackFeat.setGeometry(simplifyGeom)
    trackFeat.setStyle(new style.Style({
      stroke: new style.Stroke({
        color: this.color,
        width: 2,
      }),
    }))
    this.pointLayer = new layer.WebGLPoints({
      zIndex: this.zIndex + 2,
      source: new source.Vector({
        features: pointFeatures
      }) as any,
      style: {
        'circle-radius': ['match', ['get', 'hover_'], 1, 6, 4],
        'circle-fill-color': this.color,
        'circle-stroke-color': '#fff',
        'circle-stroke-width': 1,
      },
    })
    this.pointLayer.set('layerName', this.uuid + '_point')
    this.pointLayer.setVisible(this.showTrackPoint)
    this.map.addLayer(this.pointLayer)
    this.directionLayer = new layer.WebGLPoints({
      zIndex: this.zIndex + 1,
      source: new source.Vector({
        features: directionFeatures
      }) as any,
      style: {
        'icon-src': DirectionImg,
        'icon-color': this.color,
        'icon-rotation': ['get', 'rotation_'],
        'icon-width': [
          'interpolate',
          ['exponential', 2],
          ['zoom'],
          0, 26,
          20, 16
        ],
        'icon-height': [
          'interpolate',
          ['exponential', 2],
          ['zoom'],
          0, 26,
          20, 22
        ],
      },
    })
    this.directionLayer.set('layerName', this.uuid + '_direction')
    this.directionLayer.setVisible(this.showTrackDirection)
    this.map.addLayer(this.directionLayer)
  }
  refreshData() {
    const {simplifyGeom, pointFeatures, directionFeatures} = this.getSimplifyData()
    const trackFeat = this.trackLayer.getSource().getFeatures()[0]
    trackFeat.setGeometry(simplifyGeom)
    this.pointLayer.getSource().clear()
    this.pointLayer.getSource().addFeatures(pointFeatures)
    this.directionLayer.getSource().clear()
    this.directionLayer.getSource().addFeatures(directionFeatures)
  }
  add(newPoints) {
    newPoints.forEach((v, i) => {
      this.pointMap.set(`${v[this.keyMapper.lon]}_${v[this.keyMapper.lat]}`, v)
      this.historyPointCoordinates.push([v[this.keyMapper.lon], v[this.keyMapper.lat]])
      this.historyPoints.push(v)
    })
    this.refreshData()
  }
  update(newPoints) {
    newPoints.forEach((v, i) => {
      this.pointMap.set(`${v[this.keyMapper.lon]}_${v[this.keyMapper.lat]}`, v)
      this.realPointCoordinates.push([v[this.keyMapper.lon], v[this.keyMapper.lat]])
      this.realPoints.push(v)
    })
    this.refreshData()
  }
  setColor(val) {
    this.color = val
    this.refreshStyle()
  }
  destroy() {
    this.map.getView().un('change:resolution', this.functions.zoom)
    this.map.getLayers().getArray().filter(v => [this.uuid + '_direction', this.uuid + '_point', this.uuid + '_track'].includes(v.get('layerName'))).forEach(v => {
      this.map.removeLayer(v)
    })
  }
}
const randomColor = (opacity) => `rgba(${randomNum(0, 255)}, ${randomNum(0, 255)}, ${randomNum(0, 255)}, ${opacity ? opacity : randomNum(0.5, 1, 1)})`
const randomNum = (min = 0, max = 0, decimal= 0) => {
  // 获取数值的小数部分
  const getDecimalNum = (data: number) => {
    return Number(data.toString().split('.')[1]);
  }
  let min_z = Math.trunc(min); // 最小值的整数部分
  let max_z = Math.trunc(max); // 最大值的整数部分
  // 判断是否存在小数部分，不存在的话为0
  let min_x = isNaN(getDecimalNum(min)) ? 0 : getDecimalNum(min);  // 最小值的小数部分
  let max_x = isNaN(getDecimalNum(max)) ? 0 : getDecimalNum(max);  // 最大值的小数部分

  // 区分有小数和没小数的情况
  if (min_x > 0 || max_x > 0 || decimal > 0) {
    // 整数部分随机数
    let z = parseInt(String(Math.random() * (max_z - min_z + 1) + min_z), 10);
    // 小数部分随机数
    let x = 0;
    // 小数部分随机数最大位数
    let max_decimal = min_x.toString().length > max_x.toString().length ? min_x.toString().length : max_x.toString().length;
    max_decimal = decimal > max_decimal ? decimal : max_decimal;
    // 判断随机出的整数部分，是否等于最小值或者最大值
    if(z == min_z || z == max_z){
      if(z == min_z){
        // 整数部分随机数等于最小值，那么应该从最小值的小数部分开始，到小数位数的最大值随机就可以
        x = parseInt(String(Math.random() * (Math.pow(10, max_decimal) - min_x) + min_x), 10);
      }else{
        // 整数部分随机数等于最大值，那么应该从0开始，到最大值小数部分
        x = parseInt(String(Math.random() * (max_x + 1)), 10);
      }
    }else{
      // 整数部分在最大最小值区间的，就从0到小数位数的最大值随机就可以
      x = parseInt(String(Math.random() * (Math.pow(10, max_decimal))), 10);
    }
    return Number(`${z}.${x}`);
  } else {
    return parseInt(String(Math.random() * (max_z - min_z + 1) + min_z), 10);
  }
}
const getShapeView = (map, position, L = 600, defaultZoom = 12) => {
  const center = extent.getCenter(extent.boundingExtent(position))
  let x = 0
  let y = 0
  position.forEach(v => {
    if (Math.abs(v[0] - center[0]) > x) {
      x = Math.abs(v[0] - center[0])
    }
    if (Math.abs(v[1] - center[1]) > y) {
      y = Math.abs(v[1] - center[1])
    }
  })
  const resolution = Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)) / (L / document.body.clientWidth * document.body.clientHeight)
  if (map) {
    if (position.length > 1) {
      map.getView().animate({
        center, resolution
      })
    } else {
      map.getView().animate({
        center, zoom: defaultZoom
      })
    }
  }
  return {
    center, resolution
  }
}
const debounce = function (cb: any, ms = 0) {
  let timer: any = null
  return function () {
    if (timer) clearTimeout(timer)
    timer = setTimeout(() => {
      //  @ts-ignore
      cb.apply(this, arguments)
      timer = null
    }, ms)
  }
}
export default ShipTrack